package sound.main;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.Vector;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.openal.AL;
import org.lwjgl.openal.AL10;
import org.lwjgl.openal.ALC11;
import org.lwjgl.openal.ALC10;
import org.lwjgl.util.WaveData;

import com.jcraft.oggdecoder.OggData;
import com.jcraft.oggdecoder.OggDecoder;

import tools.Help;
import tools.Point;


public final class SoundManager {
	
	public static final int SOURCESPERBUFFER = 4;
		
	public static TreeMap<String, SourceProvider> sourceproviders;
	public static TreeMap<String, Integer> buffers;
	public static Vector<Integer> sources;
	public static void initialize() {
		try {
			AL.create();
			AL10.alDopplerFactor(10);
			AL10.alDopplerVelocity(3000);
			buffers = new TreeMap<String, Integer>();
			sourceproviders = new TreeMap<String,SourceProvider>();
			FloatBuffer listenerOri = (FloatBuffer) BufferUtils.createFloatBuffer(6).put(new float[]{0, 0, -1.0f, 0, 1.0f, 0.0f}).rewind();
			AL10.alListener(AL10.AL_ORIENTATION, listenerOri);
			AL10.alListenerf(AL10.AL_GAIN, 400); // somehow sets the global volume/distance aspect
			sources = new Vector<Integer>();
		} catch (LWJGLException e) {
			e.printStackTrace();
			return;
		}
		AL10.alGetError();
		loadSoundData();
	}
	private static void loadSoundData() {
		addBuffer("test.wav");
		addBuffer("boost.wav");
		addBuffer("beep.wav");
		addBuffer("explosion.wav");
		addBuffer("song.ogg");
		addBuffer("dronebla.ogg");
	}
	private static void loadOggData(String fname){
		
		
		OggDecoder decoder = new OggDecoder();
		try {
			InputStream in = new FileInputStream("resources/audio/"+fname);
			OggData ogg = decoder.getData(in);
			
			int b = AL10.alGenBuffers();
			AL10.alBufferData(b, ogg.channels > 1 ? AL10.AL_FORMAT_STEREO16 : AL10.AL_FORMAT_MONO16, ogg.data, ogg.rate);
			
			buffers.put(fname,b);
			
			addSourceProvider(fname);
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
	private static void addSourceProvider(String fname) {
		int sources[] = new int[SOURCESPERBUFFER];
		for(int i=0;i<sources.length;i++){
			sources[i] = getSource();
		}
		sourceproviders.put(fname, new SourceProvider(sources));
	}
	
	private static int getSource() {
		int r = AL10.alGenSources();
		sources.add(r);
		return r;
	}
	public static void setListenerPosition(Point pos, Point vel) {
		AL10.alListener3f(AL10.AL_POSITION, pos.x, pos.y, -50);
		AL10.alListener3f(AL10.AL_VELOCITY, vel.x, vel.y, 0);
	}
	private static void addBuffer(String fname) {
		if(fname.endsWith(".ogg")){
			loadOggData(fname);
			return;
		}
		int b = AL10.alGenBuffers();
		FileInputStream fin = null;
		try {
			fin = new FileInputStream("resources/audio/" + fname);
		} catch (java.io.FileNotFoundException ex) {
			ex.printStackTrace();
			return;
		}
		WaveData wf = WaveData.create(new BufferedInputStream(fin));
		try {
			fin.close();
		} catch (IOException ex) {
		}
		AL10.alBufferData(b, wf.format, wf.data, wf.samplerate);
		wf.dispose();
		buffers.put(fname, b);
		addSourceProvider(fname);
	}
	public static void setBuffer(int source, String name, float pitch, float volume) {
		if (buffers.containsKey(name)) {
			int b = buffers.get(name);
			AL10.alSourcei(source, AL10.AL_BUFFER, b);
			AL10.alSourcef(source, AL10.AL_PITCH, pitch);
			AL10.alSourcef(source, AL10.AL_GAIN, volume);
			AL10.alSourcePlay(source);
		}
		else {
			Help.p("not such sound file available! " + name);
		}
	}
	public static void end() {
		for (Map.Entry<String, Integer> b : buffers.entrySet()) {
			AL10.alDeleteBuffers(b.getValue());
		}
		for (int s : sources) {
			AL10.alDeleteSources(s);
		}
		AL.destroy();
	}
	public static void setSourcePos(int source, Point pos) {
		AL10.alSource3f(source, AL10.AL_POSITION, pos.x, pos.y, 0);
	}
	public static void setLoopSource(int source, boolean b) {
		AL10.alSourcei(source, AL10.AL_LOOPING, b ? AL10.AL_TRUE : AL10.AL_FALSE);
	}
	public static void play(int source) {
		AL10.alSourcePlay(source);
	}
	public static void pause(int source) {
		AL10.alSourcePause(source);
	}
	public static void setVolume(int sid, float f) {
		AL10.alSourcef(sid, AL10.AL_GAIN, f);
	}
	public static void setPitch(int sid, float f) {
		AL10.alSourcef(sid, AL10.AL_PITCH, f);
	}
	public static void setVolume(float volume) {
		AL10.alListenerf(AL10.AL_GAIN, 400 * volume);
	}
	
	public static int getSource(String filename) {	
		if(sourceproviders.containsKey(filename)){
			return sourceproviders.get(filename).getSource();
		}else{
			Help.p("Error: Not such sound loaded: "+filename);
		}
		return 0;
	}
}
